package com.example.springstack.springsecurity.service.impl;

import com.exampl.springstack.common.GlobalConst;
import com.example.springstack.springsecurity.entity.AuthenticationInfoVo;
import com.example.springstack.springsecurity.service.JwtService;
import io.jsonwebtoken.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;

import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import javax.servlet.http.HttpServletRequest;
import java.util.Base64;
import java.util.Date;
import java.util.HashMap;
import java.util.UUID;

/**
 * @author Qiangfanghao
 */
@Slf4j
@Service("jwtService")
public class JwtServiceImpl implements JwtService {

    /**
     * 根据用户名和密码生成
     * jwt生成token
     * @param username
     * @param currentIp
     * @return
     */
    @Override
    public AuthenticationInfoVo jwtBuild(String username, String currentIp) {
        //获取秘钥
        SecretKey secretKey = getGeneralKey();

        //token签发时间
        long now = System.currentTimeMillis();

        //过期时间 一小时
        long exp = now + 60 * 60 * 1000;

        //刷新时间 半小时
        long refreshTokenExpire = now + 30 * 60 * 1000;

        HashMap<String, Object> tokenMap = new HashMap<>(16);
        tokenMap.put("ip",currentIp);

        //建立 荷载
        JwtBuilder jwtBuilder = Jwts.builder()
                //设置jwt id jti: 唯一标识
                .setId(UUID.randomUUID().toString())
                //sub 签发对象
                .setSubject(username)
                //iat: 签发时间
                .setIssuedAt(new Date())
                //签名 设置 加密算法和 盐
                .signWith(SignatureAlgorithm.HS256, secretKey)
                //exp:过期时间 设置token 过期时间
                .setExpiration(new Date(exp))
                //自定义token信息
                .addClaims(tokenMap);

        //生成token令牌
        return new AuthenticationInfoVo(GlobalConst.AUTHORIZATION
                , jwtBuilder.compact()
                , exp
                , refreshTokenExpire);
    }

    /**
     * 登录认证 token
     * @param token
     * @param currentIp
     * @return
     */
    @Override
    public Boolean jwtAuthentication(String token,String currentIp,String currentUsername) {

        Claims body = null;

        try {
            //获取秘钥
            SecretKey generalKey = getGeneralKey();

            body = Jwts.parser()
                    //设置秘钥
                    .setSigningKey(generalKey)
                    //设置解析token
                    .parseClaimsJws(token)
                    //获取负载
                    .getBody();

            //获取到 token中的登录ip
            Object ipObj = body.get("ip");

            //获取到登录用户username
            String username = body.getSubject();

            if (ipObj == null || StringUtils.isEmpty(username)){
//                throw new PEException(ResultEnum.USER_LOGIN_TIMEOUT);
            }
            String ip = String.valueOf(ipObj);

            //校验用户名称  和 登录时候的ip
            if (!ip.equals(currentIp) || !username.equals(currentUsername)){
//                throw new PEException(ResultEnum.USER_LOGIN_TIMEOUT);
            }

        }catch (ExpiredJwtException expiredJwtException){
            log.warn("username=[{}],ip=[{}]的token过期!",currentUsername,currentIp);
            //处理token过期问题
            body =expiredJwtException.getClaims();
        } catch (Exception e){
            e.printStackTrace();
        }

        return true;
    }

    /**
     * 获取jwt 所需 当前登录ip
     * @param request
     * @return
     */
    @Override
    public String getJwtCurrentIp(HttpServletRequest request) {
        String unknown = "unknown";
        String ip = request.getHeader("x-forwarded-for");
        if(ip == null || ip.length() == 0 || unknown.equalsIgnoreCase(ip)) {
            ip = request.getHeader("Proxy-Client-IP");
        }
        if(ip == null || ip.length() == 0 || unknown.equalsIgnoreCase(ip)) {
            ip = request.getHeader("WL-Proxy-Client-IP");
        }
        if(ip == null || ip.length() == 0 || unknown.equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
        }
        return ip;
    }


    /**
     * 获取到 秘钥
     * @return
     */
    private SecretKey getGeneralKey(){

        byte[] encodedKey = Base64.getDecoder().decode(GlobalConst.JWT_KEY);

        SecretKey key = new SecretKeySpec(encodedKey, 0, encodedKey.length, "AES");
        return key;
    }

}
